package org.ala.tcp_feign_client.spring.feign.results;

import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.ala.tcp_feign_common.logs.LoggerFactory;
import org.slf4j.Logger;

/**
 * 超时检测队列
 *
 * @author 骆毅(ala)
 * @date 2024年4月25日
 */
public class TcpFeignClientResultsTimeoutQueue {
	
	
	static Logger log = LoggerFactory.getLogger("tcp-feign", "client");
	
	
	/**
	 * 公共对象
	 */
	private static TcpFeignClientResultsTimeoutQueue COMMON;
	public static TcpFeignClientResultsTimeoutQueue common(long timeout) {
		if (COMMON == null) {
			synchronized (TcpFeignClientResultsTimeoutQueue.class) {
				if (COMMON == null) {
					COMMON = new TcpFeignClientResultsTimeoutQueue(timeout);
				}
			}
		}
		return COMMON;
	}
	
	
	/**
	 * 超时检测触发任务
	 */
	protected ScheduledExecutorService scheduled;
	
	/**
	 * 超时检测队列
	 * <p>	按bing时间升序
	 */
	protected ConcurrentLinkedDeque<IBizRespResultCallback> timeoutDeque1;
	/**
	 * 超时检测队列
	 * <p>	按bing时间升序
	 */
	protected ConcurrentLinkedDeque<IBizRespResultCallback> timeoutDeque2;
	/**
	 * 1队和2队来回切
	 * <p>	true:  queue1负责吃数据，queue2正在遍历
	 * <p>	false: queue1正在遍历，queue2负责吃数据
	 */
	protected volatile boolean master = true;
	
	/**
	 * 超时时间
	 * <p>	单位：毫秒
	 * <p>	默认5s
	 */
	protected long timeout = 5000l;
	
	
	public TcpFeignClientResultsTimeoutQueue(long timeout) {
		this.timeout = timeout;
		
		timeoutDeque1 = new ConcurrentLinkedDeque<>();
		timeoutDeque2 = new ConcurrentLinkedDeque<>();
		
		scheduled = Executors.newScheduledThreadPool(1);
		scheduled.scheduleWithFixedDelay(() -> scanTimeout(), timeout/2, timeout/2, TimeUnit.MILLISECONDS);
	}
	/**
	 * 检测超时的消息
	 */
	protected void scanTimeout() {
		ConcurrentLinkedDeque<IBizRespResultCallback> deque = master ? timeoutDeque1 : timeoutDeque2;
		master = !master;
		
		long now = System.currentTimeMillis();
		while (!deque.isEmpty()) {
			IBizRespResultCallback call = deque.peekFirst();
			
			if (call.isFinished()) {deque.pollFirst(); continue;}
			
			if (now - call.bindTimestamp() < timeout) {break;}
			else {
				deque.pollFirst();

				try {
					
					call.doTimeout();
				} catch (Exception e) {
					log.warn("[TcpFeignClientResultsTimeoutQueue scanTimeout] 检测到请求超时，但执行超时失败", e);
				}
			}
		}
	}
	
	
	/**
	 * 放入超时检测
	 */
	public void appendTimeoutQueue(IBizRespResultCallback call) {
		if (master) {
			timeoutDeque1.addLast(call);
		} else {
			timeoutDeque2.addLast(call);
		}
	}
	
	
}
